#pragma once

#include "alloc.h"

namespace sfc::boxed {

template <class T = void, class A = alloc::Global>
struct Box : ptr::Unique<T> {
  static inline auto _A = A();

  using Base = ptr::Unique<T>;
  using Base::_ptr;

  explicit Box(T* p) noexcept : Base{p} {}

  Box() noexcept = default;

  Box(Box&&) noexcept = default;

  ~Box() {
    if (_ptr == nullptr) {
      return;
    }
    _A.dealloc_value(_ptr);
  }

  template <class... U>
  static auto xnew(U&&... args) -> Box {
    auto p = _A.template alloc_value<T>();
    new (p) T{static_cast<U&&>(args)...};
    return Box{p};
  }
};

template <class A>
struct Box<void, A> {
  template <class T>
  static auto from_raw(T* p) -> Box<T, A> {
    return Box<T, A>{p};
  }

  template <class T>
  static auto xnew(T val) -> Box<T, A> {
    return Box<T, A>::xnew(static_cast<T&&>(val));
  }
};

}  // namespace sfc::boxed
